using System;
using System.Xml;
using System.Text;
using System.Globalization;


namespace Waymex.Gps
{
	/// <summary>
	/// A collection of Almanac objects.
	/// </summary>
	public class AlmanacCollection : System.Collections.IEnumerable
	{
		private const string vbModule = "Almanacs";
		private const string XML_ROOT = "gpsalmanacs";
		private const string XML_ALMANAC = "gpsalmanac";

		private int m_hashCode = -1;

		private System.Collections.ArrayList mcAlmanacs;
		/// <summary>
		/// This method adds a Almanac object to the collection. 
		/// </summary>
		public void Add(Almanac almanac)
		{

			try
			{
				mcAlmanacs.Add(almanac);
			}
			catch (Exception e)
			{
				throw new InvalidDataException(e.Message, e);
			}
		}
		/// <summary>
		/// Returns the number of Almanac objects in the collection. 
		/// </summary>
		public int Count
		{
			get
			{
				try
				{
					return mcAlmanacs.Count;
				}
				catch (Exception e)
				{
					throw new InvalidDataException(e.Message, e);
				}
			}
		}

		//default indexer property
		/// <summary>
		/// Can be used to refer to a member of the collection by ordinal reference.
		/// This Property is the default indexer in C#.
		/// </summary>
		public Almanac this[int index]
		{
			get
			{
				return (Almanac)mcAlmanacs[index];
			}

		}
		/// <summary>
		/// This method is the enumerator for the collection.
		/// </summary>
		public System.Collections.IEnumerator GetEnumerator()
		{
			return mcAlmanacs.GetEnumerator();
		}
		/// <summary>
		/// Constructor for the Almanacs collection.
		/// </summary>
		public AlmanacCollection() :  base()
		{
			mcAlmanacs = new System.Collections.ArrayList();
		}
        /// <summary>
        /// Returns an XML representation of the object.
        /// </summary>
        public string ToXml()
        {
            return ToXml(String.Empty);
        }
        /// <summary>
        /// Returns an XML representation of the object.
        /// This overloaded method accepts an xslt filename which can be used to ransform the xml.
        /// </summary>
        public string ToXml(string xsltFilename)
        {
			StringBuilder strbldXML = new StringBuilder("");
            String xml = String.Empty;

			try
			{
				strbldXML.Append ("<");
				strbldXML.Append (XML_ROOT);
				strbldXML.Append (">");

				foreach(Almanac objAlmanac in mcAlmanacs)
				{
					strbldXML.Append(objAlmanac.ToXml());
				}

				strbldXML.Append ("</");
				strbldXML.Append (XML_ROOT);
				strbldXML.Append (">");

                //transform if required
                xml = strbldXML.ToString();

                if (xsltFilename.Length > 0)
                {
                    xml = Waymex.Xml.Transform.XsltTransform(xml, xsltFilename);
                }
            }
            catch (NullReferenceException e)
            { 
				throw new XmlException(e.Message, e);

			}
			return xml;
		}

		/// <summary>
		/// This method populates the object from XML.
		/// </summary>
        public void XmlLoad(string xml)
		{

			XmlDocument objDOM = new XmlDocument();

			try
			{
                objDOM.LoadXml(xml);

				if(objDOM.FirstChild.Name == XML_ROOT)
				{
					foreach(XmlNode objNode in objDOM.FirstChild.ChildNodes)
					{
						try
						{
							switch(objNode.Name)
							{
								case XML_ALMANAC:

									Almanac objAlmanac = new Almanac();
									objAlmanac.XmlLoad(objNode.OuterXml);
									mcAlmanacs.Add(objAlmanac);
									break;
							
							}
						}
						catch(NullReferenceException ex)
						{
						}
					}
				}	
			}
			catch
			{
				throw;
			}
		}
		/// <summary>
		/// This method returns a System.Data.Dataset populated with the contents of the AlmanacCollection object.
		/// The dataset includes one table called 'Almanacs'.
		///</summary>
		public System.Data.DataSet ToDataSet()
		{
			//constants for the dataset
			const string DS_ALMANACS = "Almanacs";

			//constants for the waypoints table
			const string DS_TABLE_ALMANACS = "Almanacs";
			const string DS_FIELD_ALMANAC_WEEK_NUMBER = "Week Number";
			const string DS_FIELD_ALMANAC_WEEK_NUMBER_TYPE = "System.Int16";
			const string DS_FIELD_ALMANAC_REFERENCE_TIME = "Reference Time";
			const string DS_FIELD_ALMANAC_REFERENCE_TIME_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF0 = "Clock Correction af0";
			const string DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF0_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF1 = "Clock Correction af1";
			const string DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF1_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_ECCENTRICITY = "Eccentricity";
			const string DS_FIELD_ALMANAC_ECCENTRICITY_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_SQRROOT_SMAXIS = "SqrRoot SMAxis";
			const string DS_FIELD_ALMANAC_SQRROOT_SMAXIS_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_MEAN_ANOMOLY = "Mean Anomoly";
			const string DS_FIELD_ALMANAC_MEAN_ANOMOLY_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_PERIGREE = "Perigee";
			const string DS_FIELD_ALMANAC_PERIGREE_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_RIGHT_ACENSION = "Right Ascension";
			const string DS_FIELD_ALMANAC_RIGHT_ACENSION_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_RIGHT_ACENSION_RATE = "Right Ascension Rate";
			const string DS_FIELD_ALMANAC_RIGHT_ACENSION_RATE_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_INCLINATION = "Inclination";
			const string DS_FIELD_ALMANAC_INCLINATION_TYPE = "System.Single";
			const string DS_FIELD_ALMANAC_HEALTH = "Health";
			const string DS_FIELD_ALMANAC_HEALTH_TYPE = "System.Int16";
			const string DS_FIELD_ALMANAC_SATELLITEID = "Satellite ID";
			const string DS_FIELD_ALMANAC_SATELLITEID_TYPE = "System.Int16";


			//create a new data set
			System.Data.DataSet ds = new System.Data.DataSet(DS_ALMANACS);
            ds.Locale = CultureInfo.InvariantCulture;
				
			//create the waypoints table			
			System.Data.DataTable dtA = new System.Data.DataTable(DS_TABLE_ALMANACS);
            dtA.Locale = CultureInfo.InvariantCulture;

			//add the columns to the waypoints table
			dtA.Columns.Add(DS_FIELD_ALMANAC_WEEK_NUMBER, Type.GetType(DS_FIELD_ALMANAC_WEEK_NUMBER_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_REFERENCE_TIME, Type.GetType(DS_FIELD_ALMANAC_REFERENCE_TIME_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF0, Type.GetType(DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF0_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF1 , Type.GetType(DS_FIELD_ALMANAC_CLOCK_CORRECTION_AF1_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_ECCENTRICITY, Type.GetType(DS_FIELD_ALMANAC_ECCENTRICITY_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_SQRROOT_SMAXIS, Type.GetType(DS_FIELD_ALMANAC_SQRROOT_SMAXIS_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_MEAN_ANOMOLY, Type.GetType(DS_FIELD_ALMANAC_MEAN_ANOMOLY_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_PERIGREE, Type.GetType(DS_FIELD_ALMANAC_PERIGREE_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_RIGHT_ACENSION, Type.GetType(DS_FIELD_ALMANAC_RIGHT_ACENSION_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_RIGHT_ACENSION_RATE, Type.GetType(DS_FIELD_ALMANAC_RIGHT_ACENSION_RATE_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_INCLINATION, Type.GetType(DS_FIELD_ALMANAC_INCLINATION_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_HEALTH, Type.GetType(DS_FIELD_ALMANAC_HEALTH_TYPE));
			dtA.Columns.Add(DS_FIELD_ALMANAC_SATELLITEID, Type.GetType(DS_FIELD_ALMANAC_SATELLITEID_TYPE));

			//add the data
			foreach(Almanac objAlmanac in mcAlmanacs)
			{
				object[] objRow = new object[13];
				objRow[0] = objAlmanac.WeekNumber;
				objRow[1] = objAlmanac.ReferenceTime;
				objRow[2] = objAlmanac.ClockCorrectionAF0;
				objRow[3] = objAlmanac.ClockCorrectionAF1;
				objRow[4] = objAlmanac.Eccentricity;
				objRow[5] = objAlmanac.SqrRootSemiMajorAxis;
				objRow[6] = objAlmanac.MeanAnomaly;
				objRow[7] = objAlmanac.Perigee;
				objRow[8] = objAlmanac.RightAscension;
				objRow[9] = objAlmanac.RightAscensionRate;
				objRow[10] = objAlmanac.Inclination;
				objRow[11] = objAlmanac.Health;
				objRow[12] = objAlmanac.SatelliteId;				//byte array in Waypoint object

				dtA.Rows.Add(objRow);
				
			}
			//tables populated so add the tables to the dataset
			ds.Tables.Add(dtA);

			return ds;
		}
        /// <summary>
        /// Overridden method. Returns true of the values of each
        /// of the properties are equal in value.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>boolean</returns>
        public override bool Equals(object obj)
		{
			try
			{
				AlmanacCollection objAlmanacs = null;

				//check the type first
				if(obj.GetType() != this.GetType() )
					return false;

				//type ok so cast
				objAlmanacs = (AlmanacCollection)obj;

				//if the number in each collection is different then exit
				int w1Count = mcAlmanacs.Count;
				int w2Count = objAlmanacs.Count;
				if(w1Count != w2Count)
					return false;

				//both with the same number of elements so chech each one in turn
				for( int index = 0; index < w1Count; index++ )
				{
					if( !objAlmanacs[index].Equals(mcAlmanacs[index]) )
						return false;
				}
			}
			catch
			{
                throw;
			}
			finally
			{}

			//if we got here then they must match
			return true;
		}
        /// <summary>
        /// Overridden function. Retrieves a value that indicates the hash code value for the object.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
		{
			//this value will be used for the overriden GetHashCode function and should
			//ensure that two object that are the same return the same Hash Code.
			//the hash code has to be imutable to is stored in a member variable.
			if( m_hashCode <= 0 )
				m_hashCode = this.ToXml().GetHashCode();
	
			return m_hashCode;
		}
	}
}
